# -*- encoding: utf-8 -*-

from turbogears import controllers, expose, validate, validators
from model import *
import sqlobject
from turbogears import identity, redirect, url
from cherrypy import request, response
from turbogears.widgets import WidgetsList, TextField, HiddenField, FileField, CalendarDatePicker, TextArea, RemoteForm, TableForm
from turbogears.feed import FeedController
import datetime
import time
import cherrypy
import urllib

from elementtree.ElementTree import fromstring

GOOGLE_API_KEY = cherrypy.config.get("miestas.google_api_key", "")

class MarkerFormField(WidgetsList):
    title = TextField(label = 'SSID'.decode('utf-8'), name = 'Title')
    marker = HiddenField("Marker")
    longitude = HiddenField(name = 'Longitude')
    latitude = HiddenField(name = "Latitude")
    city = TextField(label = 'Vietovė'.decode('utf-8'), name = 'City')
    street = TextField(label = 'Gatvė'.decode('utf-8'), name = 'Street')
    number = TextField(label = 'Numeris', name = 'Number')
    description = TextArea(label = 'Aprašas'.decode('utf-8'), name = 'Description')

class DeleteFormField(WidgetsList):
    marker = HiddenField("Marker")

class AddCommentFormField(WidgetsList):
    marker = HiddenField('Marker')
    author = TextField(label = 'Autorius', name = 'Author')
    email = TextField(label = 'E-paštas'.decode('utf-8'), name = 'Email')
    homepage = TextField(label = 'Namų puslapis'.decode('utf-8'), name = 'Homepage')
    text = TextArea(label = 'Tekstas', name = 'Text')

class ImportFormField(WidgetsList):
    file = FileField(label = 'Failas', name = 'File')

class Feed(FeedController):

    def get_feed_data(self):
        entries = []

        for marker in Marker.select(orderBy=Marker.q.datetime).reversed()[:20]:
            entry = {}
            entry["updated"] = marker.datetime
            entry["title"] = marker.title
            entry["published"] = marker.datetime
            if marker.author is not None:
                entry["author"] = {"name": marker.author.display_name, "email": marker.author.email_address}
            entry["link"] = 'http://%s/?id=%d&lat=%f&lng=%f' % (cherrypy.request.headers.get('Host', ''), marker.id, marker.latitude, marker.longitude)
            entry["summary"] = marker.description
            entries.append(entry)

        return dict(
            title = "Miestas",
            link = 'http://' + cherrypy.request.headers.get('Host', ''),
            author = {"name": "dado1945", "email": "dado1945@gmail.com"},
            id = 'http://' + cherrypy.request.headers.get('Host', ''),
            entries = entries
        )


class FeedComments(FeedController):

    def get_feed_data(self):
        entries = []

        for comment in Comment.select(orderBy=Comment.q.datetime).reversed()[:20]:
            entry = {}
            entry["updated"] = comment.datetime
            entry["title"] = comment.marker.title
            entry["published"] = comment.datetime
            entry["author"] = {"name": comment.author, "email": comment.email}
            entry["link"] = 'http://%s/?id=%d&lat=%f&lng=%f' % (cherrypy.request.headers.get('Host', ''), comment.marker.id, comment.marker.latitude, comment.marker.longitude)
            entry["summary"] = comment.text
            entries.append(entry)

        return dict(
            title = "Miestas komentarai",
            link = 'http://' + cherrypy.request.headers.get('Host', ''),
            author = {"name": "dado1945", "email": "dado1945@gmail.com"},
            id = 'http://' + cherrypy.request.headers.get('Host', ''),
            entries = entries
        )

def strongly_expire(func):
    """Decorator that sends headers that instruct browsers and proxies not to cache.
    """
    def newfunc(*args, **kwargs):
        cherrypy.response.headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT'
        cherrypy.response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
        cherrypy.response.headers['Pragma'] = 'no-cache'
        return func(*args, **kwargs)
    return newfunc

class Admin(controllers.Controller):
    """ Admin controls. """

    @expose(template="miestas.templates.admin")
    def index(self):
        import_form = TableForm(name='ImportForm', fields=ImportFormField(), submit_text='Importuoti')
        return dict(import_form=import_form)

    @expose()
    def importxml(self, **kw):
        xml_data = fromstring(kw['File'].file.read())

        if xml_data.tag == 'DataSet1':
            for node in xml_data:
                if node.tag == 'AP':
                    WEP = node.find('WEP')
                    if WEP is not None and WEP.text.lower() == 'off':
                        lat = node.find('Lat').text
                        lng = node.find('Lon').text

                        u = urllib.urlopen("http://ws.geonames.org/findNearbyPlaceNameJSON?lat=%s&lng=%s" % (lat, lng))
                        data = eval(u.read().strip())
                        u.close()

                        if 'geonames' in data and len(data['geonames']) > 0:
                            city_name = data['geonames'][0]['name'].decode('utf-8')
                        else:
                            city_name = ''

                        try:
                            city = City.byTitle(city_name)
                        except sqlobject.SQLObjectNotFound:
                            city = City(title=city_name)

                        try:
                            street = Street.byTitle('')
                        except sqlobject.SQLObjectNotFound:
                            street = Street(title='')

                        marker = Marker(
                                title=node.find('SSID').text,
                                datetime=datetime.datetime.utcnow(),
                                latitude=float(lat),
                                longitude=float(lng),
                                author=identity.current.user,
                                city=city,
                                street=street,
                                number='',
                                description='',
                                rating_value=1,
                                external_rating_value=0
                                )
                        Rating(marker=marker, user=identity.current.user, positive=True)

        raise redirect('/admin')

class Root(controllers.RootController):
    """ Root controller for 'miestas' """

    feed = Feed()
    feedcomments = FeedComments()
    admin = Admin()

    @expose(template="miestas.templates.welcome")
    def index(self, id=None, level=None, lat=None, lng=None):
        marker_form = RemoteForm(
                name="MarkerForm",
                fields=MarkerFormField(),
                submit_text='Keisti')
        delete_form = RemoteForm(
                name="DeleteForm",
                fields=DeleteFormField(),
                submit_text='Trinti')
        add_comment_form = RemoteForm(
                name="AddCommentForm",
                fields=AddCommentFormField(),
                submit_text='Komentuoti')

        return dict(marker_form=marker_form,
                delete_form=delete_form,
                add_comment_form=add_comment_form,
                level=level, lat=lat, lng=lng, id=id,
                google_api_key=GOOGLE_API_KEY)

    @expose("json")
    @strongly_expire
    def markerlist(self):
        """ This functions returns markers list. """
        markers = []
        for marker in Marker.select():
            rated_item = Rating.selectBy(marker=marker, user=identity.current.user)
            rated = ''
            if (rated_item.count() > 0):
                rated = str(rated_item[0].positive)

            markers.append({'id': marker.id,
                'latitude': marker.latitude,
                'longitude': marker.longitude,
                'modify': (marker.author is not None and marker.author == identity.current.user) or (not identity.current.anonymous and not marker.author),
                'title': marker.title,
                'city': marker.city.title,
                'street': marker.street.title,
                'number': marker.number,
                'description': marker.description,
                'rating': marker.rating_value,
                'rated': rated,
                'external_rating': marker.external_rating_value})
        return dict(markers=markers)

    @expose("json")
    def reverse_geocode(self, lat, lng):
        """ Get reverse geocoding information. """
        u = urllib.urlopen("http://ws.geonames.org/findNearbyPlaceNameJSON?lat=%s&lng=%s" % (lat, lng))
        data = eval(u.read().strip())
        u.close()

        if 'geonames' in data and len(data['geonames']) > 0:
            city_name = data['geonames'][0]['name'].decode('utf-8')
            country_name = data['geonames'][0]['countryName']
        else:
            city_name = ''
            country_name = 'LT'

        return dict(lat=lat, lng=lng,
                name=city_name,
                countryName=country_name)

    @expose("json")
    def savemarkerform(self, **kw):
        """ Save marker point."""
        try:
            marker_id = int(kw['Marker'])
        except ValueError:
            marker_id = 0

        if marker_id == 0:
            try:
                city = City.byTitle(kw['City'])
            except sqlobject.SQLObjectNotFound:
                city = City(title=kw['City'])

            try:
                street = Street.byTitle(kw['Street'])
            except sqlobject.SQLObjectNotFound:
                street = Street(title=kw['Street'])

            marker = Marker(
                    title=kw['Title'],
                    datetime=datetime.datetime.utcnow(),
                    latitude=float(kw['Latitude']),
                    longitude=float(kw['Longitude']),
                    author=identity.current.user,
                    city=city,
                    street=street,
                    number=kw['Number'],
                    description=kw['Description'],
                    rating_value=0,
                    external_rating_value=0
                    )
        else:
            try:
                city = City.byTitle(kw['City'])
            except sqlobject.SQLObjectNotFound:
                city = City(title=kw['City'])

            try:
                street = Street.byTitle(kw['Street'])
            except sqlobject.SQLObjectNotFound:
                street = Street(title=kw['Street'])

            marker = Marker.get(marker_id)
            if not marker.author or identity.current.user == marker.author:
                marker.title = kw['Title']
                marker.author = identity.current.user
                marker.city = city
                marker.street = street
                marker.number = kw['Number']
                marker.description = kw['Description']

        return dict(id=marker.id,
                title=kw['Title'],
                city=kw['City'],
                street=kw['Street'],
                number=kw['Number'],
                description=kw['Description'])

    @expose("json")
    def update_marker_pos(self, id, lng, lat):
        """ Updates marker position. """
        if len(id) != 0:
            marker = Marker.get(int(id))
            if not marker.author or identity.current.user == marker.author:
                marker.longitude = float(lng)
                marker.latitude = float(lat)
        return dict()

    @expose("json")
    def delete_marker(self, **kw):
        try:
            marker_id = int(kw['Marker'])
        except ValueError:
            return dict()

        if marker_id > 0:
            marker = Marker.get(marker_id)
            if not marker.author or identity.current.user == marker.author:
                for comment in marker.comments:
                    comment.destroySelf()
                for rating in marker.rating:
                    rating.destroySelf()
                marker.destroySelf()
        return dict()

    def construct_comments(self, marker):
        """ Construct list of comments. """
        comments = [{'id': comment.id,
                     'author': comment.author,
                     'homepage': comment.homepage,
                     'text': comment.text,
                     'datetime': time.strftime("%a, %d %b %Y %H:%M:%S GMT", comment.datetime.timetuple())
                    } for comment in marker.comments]
        return comments

    @expose("json")
    def get_comments(self, id):
        """ Get comments for marker. """
        if len(id) != 0:
            marker = Marker.get(int(id))
            return dict(comments=self.construct_comments(marker))
        return dict(comments=[])

    @expose("json")
    def add_comment(self, **kw):
        """ Add comment. """
        try:
            marker_id = int(kw['Marker'])
        except ValueError:
            return dict(comment=[])

        if marker_id > 0:
            marker = Marker.get(marker_id)
            comment = Comment(
                    author=kw['Author'],
                    homepage=kw['Homepage'],
                    email=kw['Email'],
                    text=kw['Text'],
                    datetime=datetime.datetime.utcnow(),
                    marker=marker)
            return dict(comments=self.construct_comments(marker))
        return dict(comments=[])

    @identity.require(identity.in_group("miestas"))
    @expose("json")
    def delete_comment(self, id):
        """ Delete comments. """
        if len(id) > 0:
            comment = Comment.get(int(id))
            marker = comment.marker
            comment.destroySelf()
            return dict(comments=self.construct_comments(marker))
        return dict(comments=[])

    @expose("json")
    def rate_external(self, id, rating):
        """ Rate external. """
        if len(id) != 0:
            marker = Marker.get(int(id))
            marker.external_rating_value += int(rating)
            return dict(rating=marker.external_rating_value)
        return dict(rating=0)

    @identity.require(identity.in_group("miestas"))
    @expose("json")
    def rate_internal(self, id, rating):
        """ Rate internal. """
        if len(id) != 0:
            marker = Marker.get(int(id))

            rated_item = Rating.selectBy(marker=marker, user=identity.current.user)
            if (rated_item.count() > 0):
                rated_item = rated_item[0]
                rate = rated_item.positive
                if int(rating) == -1 and rate:
                    rated_item.positive = False
                    marker.rating_value -= 2
                elif int(rating) == 1 and not rate:
                    rated_item.positive = True
                    marker.rating_value += 2
            else:
                Rating(marker=marker, user=identity.current.user, positive=int(rating) == 1)
                marker.rating_value += int(rating)

            return dict(rating=marker.rating_value, rated=str(int(rating) == 1))
        return dict(rating=0)

    @expose(template="miestas.templates.login")
    def login(self, forward_url=None, previous_url=None, *args, **kw):
        """ Login. """

        if not identity.current.anonymous \
            and identity.was_login_attempted() \
            and not identity.get_identity_errors():
            raise redirect(forward_url)

        forward_url=None
        previous_url= request.path

        if identity.was_login_attempted():
            msg=_("The credentials you supplied were not correct or "
                   "did not grant access to this resource.")
        elif identity.get_identity_errors():
            msg=_("You must provide your credentials before accessing "
                   "this resource.")
        else:
            msg=_("Please log in.")
            forward_url= request.headers.get("Referer", "/")

        response.status=403
        return dict(message=msg, previous_url=previous_url, logging_in=True,
                    original_parameters=request.params,
                    forward_url=forward_url)

    @expose()
    def logout(self):
        identity.current.logout()
        raise redirect("/")
